package loader

import (
	"kumachan/standalone/util/richtext"
	"kumachan/interpreter/lang/common/source"
	"kumachan/interpreter/lang/textual/parser"
)


type Error struct {
	Context   Context
	Concrete  ConcreteError
}

type ConcreteError interface { LoaderError() }

func (e E_ReadFileFailed) LoaderError() {}
type E_ReadFileFailed struct {
	FilePath string
	Message  string
}
func (e E_StandaloneImported) LoaderError() {}
type E_StandaloneImported struct {}
func (e E_ParseFailed) LoaderError() {}
type E_ParseFailed struct {
	ParserError  *parser.Error
}
func (e E_NameConflict) LoaderError() {}
type E_NameConflict struct {
	ModuleName  string
	FilePath1   string
	FilePath2   string
}
func (e E_CircularImport) LoaderError() {}
type E_CircularImport struct {
	ModuleName  string
}
func (e E_ConflictAlias) LoaderError() {}
type E_ConflictAlias struct {
	LocalAlias  string
}
func (e E_DuplicateImport) LoaderError() {}
type E_DuplicateImport struct {
	ModuleName  string
}
func (e E_InvalidService) LoaderError() {}
type E_InvalidService struct {
	Reason  string
}

func (err *Error) Desc() richtext.Block {
	var msg richtext.Block
	const NORMAL = richtext.TAG_ERR_NORMAL
	const INLINE = richtext.TAG_ERR_INLINE
	const NOTE = richtext.TAG_ERR_NOTE
	msg.WriteLine("**")
	switch e := err.Concrete.(type) {
	case E_ReadFileFailed:
		msg.WriteSpan("Cannot open source file:", NORMAL)
		msg.WriteSpan(e.FilePath, INLINE)
		msg.WriteLine(e.Message, NORMAL)
	case E_StandaloneImported:
		msg.WriteSpan("Standalone source files are not importable", NORMAL)
	case E_ParseFailed:
		msg.Append(e.ParserError.Message())
	case E_NameConflict:
		msg.WriteSpan("The module name", NORMAL)
		msg.WriteSpan(e.ModuleName, INLINE)
		msg.WriteSpan("is used by both source files", NORMAL)
		msg.WriteSpan(e.FilePath2, INLINE)
		msg.WriteSpan("and", NORMAL)
		msg.WriteSpan(e.FilePath1, INLINE)
	case E_CircularImport:
		msg.WriteSpan("Circular import of module", NORMAL)
		msg.WriteSpan(e.ModuleName, INLINE)
	case E_ConflictAlias:
		msg.WriteSpan("A module already imported as name", NORMAL)
		msg.WriteSpan(e.LocalAlias, INLINE)
		msg.WriteSpan("in current module", NORMAL)
	case E_DuplicateImport:
		msg.WriteSpan("Duplicate import of module", NORMAL)
		msg.WriteSpan(e.ModuleName, INLINE)
	case E_InvalidService:
		msg.WriteSpan("Invalid service module:", NORMAL)
		msg.WriteSpan(e.Reason, NOTE)
	default:
		panic("unknown error kind")
	}
	return msg
}

func (err *Error) Message() richtext.Block {
	var ctx = err.Context
	return ctx.WrapErrorDesc((func() richtext.Block {
		if (ctx.ImportPoint != source.Location {}) {
			return ctx.ImportPoint.FormatMessage(err.Desc())
		} else {
			return err.Desc()
		}
	})())
}

func (err *Error) LocatableMessage() (*source.LocatableErrorMessage, bool) {
	var ctx = err.Context
	if (ctx.ImportPoint != source.Location {}) {
		return &source.LocatableErrorMessage {
			Description: err.Desc(),
			Location:    ctx.ImportPoint,
		}, true
	} else {
		return nil, false
	}
}

func (err *Error) Error() string {
	return err.Message().RenderPlainText()
}


